//-----------------------------------------------------------------------------
//
// @file	AnimChannel.h
// @brief	Anim Channel
// @author	Vinod Melapudi
// @date	13Aug2011
//
//-----------------------------------------------------------------------------

#ifndef _ANIMCHANNEL_H
#define _ANIMCHANNEL_H

#include <vector>
#include <assert.h>

#include "Utils.h"




// Anim channel (with set of keyframes)
class AnimChannel
{
public:
	AnimChannel():	m_dirty(false)	{}

	// Add a new value
	void AddValue( float time, float value, bool valid_params=false, float param1_time=0.f, float param1_value=0.f, float param2_time=0.f, float param2_value=0.f )
	{
		m_dirty = true;
		m_times.push_back( time );
		m_values.push_back( value );

		if ( valid_params )
		{
			m_param1_times.push_back( param1_time );
			m_param1_values.push_back( param1_value );
			m_param2_times.push_back( param2_time );
			m_param2_values.push_back( param2_value );
		}
	}


	// Generate tangents
	void GenerateTangents()
	{
		// Using catmull rom method
		// Reference : http://www.gamedev.net/page/resources/_/technical/game-programming/smooth-interpolation-of-irregularly-spaced-keyf-r1497


		// Clear existing values
		assert( m_times.size() == m_values.size() );

		m_param1_times.clear();
		m_param1_values.resize( m_values.size() );
		m_param2_times.clear();
		m_param2_values.resize( m_values.size() );


		// 1st frame
		m_param1_values[0] = ( m_values[1] - m_values[0] ) / ( m_times[1] - m_times[0] );
		m_param2_values[0] = m_param1_values[0];

		if ( m_values.size() == 2 )
			return;

		// Last frame
		size_t last = m_values.size() - 1;
		m_param1_values[last] = ( m_values[last] - m_values[last-1] ) / ( m_times[last] - m_times[last-1] );
		m_param2_values[last] = m_param1_values[last];

		// Middle frames
		for( size_t i=1; i < last; i++ )
		{
			m_param1_values[i] = ( m_values[i+1] - m_values[i-1] ) / ( m_times[i+1] - m_times[i-1] );
			m_param2_values[i] = m_param1_values[i];
		}
	}


	// Get interpolated value at given time
	// Reference : http://www.gamedev.net/page/resources/_/technical/game-programming/smooth-interpolation-of-irregularly-spaced-keyf-r1497
	float GetValue( float time, INTERP_TYPE interp_type )
	{
		if ( m_values.size() == 0 || m_times.size() == 0 )
			return 0;


		// Check boundaries
		if ( time <= m_times[0] )
			return m_values[0];

		if ( time >= m_times.back() )
			return m_values.back();

		assert( m_values.size() > 1 );
		assert( m_values.size() == m_times.size() );


		// Find the keyframes around time to interpolate
		size_t key_frame_idx = 0;
		for( key_frame_idx=0; key_frame_idx < m_times.size()-1; key_frame_idx++ )
		{
			if ( m_times[key_frame_idx] < time && m_times[key_frame_idx+1] > time )
			{
				break;
			}
		}


		// Get time fraction
		float f = Param( m_times[key_frame_idx], m_times[key_frame_idx+1], time );


		float kfv0 = m_values[key_frame_idx];
		float kfv1 = m_values[key_frame_idx+1];

		// Interpolate
		if ( interp_type == INTERP_LINEAR )
		{
			return Lerp( kfv0, kfv1, f );
		}
		else if ( interp_type == INTERP_QUADRATIC )
		{
			return Lerp( kfv0, kfv1, f*f );
		}
		else if ( interp_type == INTERP_CUBIC || interp_type == INTERP_CARDINAL || interp_type == INTERP_HERMITE )
		// Reference : http://www.gamedev.net/page/resources/_/technical/game-programming/smooth-interpolation-of-irregularly-spaced-keyf-r1497
		// Reference : http://www.khronos.org/files/collada_spec_1_4.pdf
		// INTERP_CUBIC,		Blending end points with Hermite basis
		// INTERP_CARDINAL,		Blending with Hermite basis using Catmul-Rom tangents built from end points
		// INTERP_HERMITE,		Tangents provided
		{
			float f_sqr = f * f;
			float f_cube = f_sqr * f;

			// Hermite basis
			float h00=0, h01=0, h10=0, h11=0;
			h00 = 2 * f_cube - 3 * f_sqr + 1;
			h01 = 3 * f_sqr - 2 * f_cube;

			// Calc extra basis for tangents
			if ( interp_type == INTERP_CARDINAL || interp_type == INTERP_HERMITE )
			{
				h10 = f_cube - 2 * f_sqr + f;
				h11 = f_cube - f_sqr;
			}

			float out_tangent = m_param2_values[key_frame_idx];
			float in_tangent = m_param1_values[key_frame_idx+1];

			return kfv0 * h00 + kfv1 * h01 + out_tangent * h10 + in_tangent * h11;
		}
		// Reference : http://www.khronos.org/files/collada_spec_1_4.pdf
		else if ( interp_type == INTERP_BEZIER )
		{

		}

		return 0;
	}


private:
	std::vector<float>		m_times;
	std::vector<float>		m_values;
	std::vector<float>		m_param1_times;		// Could be in-tangent or control point 1
	std::vector<float>		m_param1_values;		
	std::vector<float>		m_param2_times;		// Could be out-tangent or control point 2
	std::vector<float>		m_param2_values;

	bool					m_dirty;
};

#endif